using System;
using System.Drawing;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace MetaWorld
{
	/// <summary>
	/// Specialized SceneGraphNode describing a light location. The problem is that
	/// lights don't actually exist as separate objects in DirectX - they're just
	/// rendering settings on the Scene object. So we fake it: when this node's
	/// RenderSelf is called, it just modifies the global settings. :)
	/// </summary>
	abstract public class LightNode : SceneGraphNode
	{
		/// <summary>
		/// Index of the light described by this node. The number of lights
		/// is dependent on the rendering device (e.g. the video card).
		/// </summary>
		public int LightIndex;

		/// <summary>
		/// Constructor. Requires specification of which light will be controlled
		/// by this node.
		/// </summary>
		/// <param name="index"></param>
		public LightNode (int index) { 
			LightIndex = index;
		}

	}

	/// <summary>
	/// Specialized SceneGraphNode describing a point light. See LightNode comments
	/// for more detailed information about how light nodes work.
	/// </summary>
	public class PointLight : LightNode 
	{
		/// <summary>
		/// Point light range
		/// </summary>
		public float Range;

		/// <summary>
		/// Point light attenuation
		/// </summary>
		public float Attenuation;
		
		/// <summary>
		/// Constructor. Takes the index of the light to be modified by this node,
		/// as well as the range and attenuation of the light source.
		/// </summary>
		/// <param name="index"></param>
		/// <param name="range"></param>
		/// <param name="attenuation"></param>
		public PointLight (int index, float range, float attenuation) : base (index) { 
			Range = range;
			Attenuation = attenuation;
		}

		/// <summary>
		/// Constructor. Takes the index of the light to be modified by this node.
		/// Uses default attenuation and large range.
		/// </summary>
		/// <param name="index"></param>
		public PointLight (int index) : this (index, 0.7f, 1000f) { }

		/// <summary>
		/// Override the initialization function
		/// </summary>
		/// <param name="scene"></param>
		public override void Begin (Scene scene)
		{
			base.Begin (scene);
			Light light = scene.Device.Lights[LightIndex];
			
			light.Type = LightType.Point;
			light.Range = Range;
			light.Attenuation0 = Attenuation;
			light.Update ();

			scene.TurnLightOn (LightIndex);
		}


		/// <summary>
		/// "Rendering" function, which actually just sets the global position
		/// of the specified light to the current transformation position.
		/// </summary>
		public override void RenderSelf()
		{
			base.RenderSelf ();
			Device device = Scene.Device;
			Light light = device.Lights [LightIndex];
			light.DiffuseColor = device.Material.DiffuseColor;

			Matrix pos = device.Transform.World;
			light.Position = new Vector3 (pos.M41, pos.M42, pos.M43);

			light.Enabled = true;
			light.Update ();
		}
	}



	/// <summary>
	/// The camera node type sets the camera position based on the transformations
	/// applied to it. 
	/// 
	/// The default camera is located at (0, 0, -10), looking at (0, 0, 0), 
	/// and pointing up the y axis, i.e. (0, 1, 0). 
	/// 
	/// </summary>
	public class CameraNode : SceneGraphNode
	{
		/// <summary>
		/// Constructor.
		/// </summary>
		public CameraNode () { }

		/// <summary>
		/// Rendering function: takes the present transformation, 
		/// and applies it to the camera.
		/// </summary>
		public override void RenderSelf()
		{
			base.RenderSelf ();

			// Get the transform and apply
			Matrix pos = this.Scene.Device.Transform.World;
			this.Scene.CameraPosition = new Vector3 (pos.M41, pos.M42, pos.M43);
		}
	}



	/// <summary>
	/// The focus node type sets the camera focus based on the transformations
	/// applied to it. 
	/// 
	/// The default camera is located at (0, 0, -10), looking at (0, 0, 0), 
	/// and pointing up the y axis, i.e. (0, 1, 0). 
	/// 
	/// </summary>
	public class FocusNode : SceneGraphNode
	{
		/// <summary>
		/// Constructor.
		/// </summary>
		public FocusNode () { }

		/// <summary>
		/// Rendering function: takes the present transformation, 
		/// and applies it to the camera.
		/// </summary>
		public override void RenderSelf()
		{
			base.RenderSelf ();

			// Get the transform and apply
			Matrix pos = this.Scene.Device.Transform.World;
			this.Scene.CameraTarget = new Vector3 (pos.M41, pos.M42, pos.M43);
		}
	}


	/// <summary>
	/// The up node type sets the camera position based on the transformations
	/// applied to it. 
	/// 
	/// The default camera is located at (0, 0, -10), looking at (0, 0, 0), 
	/// and pointing up the y axis, i.e. (0, 1, 0). 
	/// 
	/// </summary>
	public class UpNode : SceneGraphNode
	{
		/// <summary>
		/// Constructor.
		/// </summary>
		public UpNode () { }

		/// <summary>
		/// Rendering function: takes the present transformation, 
		/// and applies it to the camera.
		/// </summary>
		public override void RenderSelf()
		{
			base.RenderSelf ();

			// Get the transform and apply
			Matrix pos = this.Scene.Device.Transform.World;
			this.Scene.CameraOrientation = new Vector3 (pos.M41, pos.M42, pos.M43);
		}
	}




}
